`default_nettype none
`timescale 1ns / 1ps

module Controller ( clk2xin,
					dout,
					hi_in,
					hi_out,
					hi_inout,
					hi_muxsel,
					i2c_scl,
					i2c_sda,
					sdram_clk_out,
					sdram_cke,
					sdram_cs_n,
					sdram_we_n,
					sdram_cas_n,
					sdram_ras_n,
					sdram_ldqm,
					sdram_udqm,
					sdram_ba,
					sdram_a,
					sdram_d
					);

/* clocks */
input wire			clk2xin;    // 166 MHz clock

/* OK host interface */
input  wire [7:0] 	hi_in;
output wire [1:0] 	hi_out;
inout  wire [15:0] 	hi_inout;
output wire        	hi_muxsel;
output wire        	i2c_scl;
output wire        	i2c_sda;

assign hi_muxsel = 1'b0;
assign i2c_sda = 1'bz;
assign i2c_scl = 1'bz;

/* SDRAM */
output wire        	sdram_clk_out;
output wire        	sdram_cke;
output wire        	sdram_cs_n;
output wire        	sdram_we_n;
output wire        	sdram_cas_n;
output wire        	sdram_ras_n;
output wire        	sdram_ldqm;
output wire        	sdram_udqm;
output wire [1:0]  	sdram_ba;
output wire [12:0] 	sdram_a;
inout  wire [15:0] 	sdram_d;

/* serial output */
output wire	[3:0] 	dout;

//------------------------------------------------------------------------
// internal connections
//------------------------------------------------------------------------

// DCM clocks
wire			clk1x; // 12ns (83.3MHz) DCM clock: decoder base clock
wire			clk2x; // 6ns (166.6MHz) DCM clock: serializer stage 1 clock
wire			clk4x; // 3ns (333MHz) DCM clock: serializer stage 2 clock
wire			sdramclk; // 9ns (111.1MHz) DCM clock

wire			clk2xindcm; // clock route between input clock buffer and DCM (necessary only because DCM is on other side of FPGA)
wire			clk1xdcm; // clock route between DCM output and buffer
wire			clk2xdcm;
wire			clk4xdcm;
wire			clkfxdcm;

// Opal Kelly Host Interface internal connections
parameter N = 2;
wire			ti_clk;
wire [30:0]		ok1;
wire [16:0]		ok2;
wire [17*N-1:0]	ok2x;

// pipe
wire [15:0]		pipe_dout;

// FIFOs
reg				pipe_fifo_rst;
wire			pipe_fifo_wr_en;
wire			pipe_fifo_rd_en;
wire [15:0]		pipe_fifo_dout;
wire			pipe_fifo_full;
wire			pipe_fifo_empty;

reg				decoder_fifo_rst;
wire			decoder_fifo_rd_en;
wire			decoder_fifo_wr_en;
wire [63:0]		decoder_fifo_dout;
wire			decoder_fifo_full;
wire [15:0]		decoder_fifo_din;
wire			decoder_fifo_underflow;



// decoder
wire			decoder_reset;
wire [3:0]		decoder_reset_value;
wire [31:0]		decoder_dout;

// sdramctrl
reg				sdramctrl_reset;
wire			cmd_done;

// controller
wire  [2:0] 	command;
reg  [14:0] 	write_address;
reg  [14:0] 	read_address;
reg 			cmd_pagewrite;
reg 			cmd_pageread;
wire 			cmd_ack;

reg [1:0]		state;


parameter CMD_IDLE        = 3'd0;
parameter CMD_RUN         = 3'd1;
parameter CMD_LOAD        = 3'd2;
parameter CMD_RESET_READ  = 3'd3;
parameter CMD_RESET_SDRAM = 3'd4;
parameter CMD_RESET_WRITE = 3'd5;



parameter IDLE = 2'd0;
parameter LOAD = 2'd1;
parameter RUN  = 2'd2;
parameter BUSY = 2'd3;


//------------------------------------------------------------------------
// transfer negotiator
//------------------------------------------------------------------------

always @(posedge sdramclk ) begin

cmd_pagewrite <=1'b0;
cmd_pageread <=1'b0;

case ( state )
// synthesis attribute init of state is 0

	IDLE: begin
		
	    pipe_fifo_rst <= 1'b0;
	    decoder_fifo_rst <= 1'b0;
    	sdramctrl_reset <= 1'b0;
		case ( command )
			CMD_RESET_SDRAM: begin			
		    	sdramctrl_reset <= 1'b1;
		    	state <= IDLE;			    
			end
			CMD_RESET_WRITE: begin
		    	pipe_fifo_rst <= 1'b1;
		    	write_address <= 0;
		    	state <= IDLE;			    
			end
			CMD_RESET_READ: begin
			    decoder_fifo_rst <= 1'b1;
			    read_address <= 0;
			    state <= IDLE;    
			end
			CMD_LOAD: begin
				if ( ~pipe_fifo_empty ) begin
				    state <= LOAD;
				end
			end
			CMD_RUN: begin
				if ( ~decoder_fifo_full ) begin
					state <= RUN;
				end
			end	
			default:
			    state <= IDLE;
			endcase
	end

	LOAD: begin
	    cmd_pagewrite <= 1'b1;
	    state <= LOAD;
		if (cmd_ack == 1'b1) begin
			write_address <= write_address + 1;
			state <= BUSY;
		end
	end

	RUN: begin
	    cmd_pageread <= 1'b1;
	    state <= RUN;
		if (cmd_ack == 1'b1) begin
			if ( read_address == write_address - 1 ) begin
				read_address <= 0;			    
			end else begin
				read_address <= (read_address + 1);
			end
			state <= BUSY;
		end
	end

	BUSY: begin
		state <= BUSY;
		if (cmd_done == 1'b1) begin
			state <= IDLE;
		end
	end
	
	endcase
end

//------------------------------------------------------------------------
// fifo between SDRAM and decoder
//------------------------------------------------------------------------
fifo16w64r_2047 decoder_fifo (
	.rst( decoder_fifo_rst ),
	.wr_clk( ~sdramclk ),
	.rd_clk( clk1x ),
	.din( decoder_fifo_din ),
	.wr_en( decoder_fifo_wr_en ),
	.rd_en( decoder_fifo_rd_en ),
	.dout( decoder_fifo_dout ),
	.prog_full_thresh( 11'd1535 ), // limit is 10'd1536 however, there is one cycle latency
	.prog_full( decoder_fifo_full ),
	.underflow( decoder_fifo_underflow ),
	.empty( ),
	.full( ) );

//------------------------------------------------------------------------
// fifo between SDRAM and pipe
//------------------------------------------------------------------------
fifo16w16r_2047 pipe_fifo (
	.rst( pipe_fifo_rst ),
	.wr_clk( ti_clk ),
	.rd_clk( ~sdramclk ),
	.wr_en( pipe_fifo_wr_en ),
	.rd_en( pipe_fifo_rd_en ),
	.din( pipe_dout ),
	.dout( pipe_fifo_dout ),
	.prog_full_thresh( 11'd1535 ), // asserted by write operation when word count >= this, one cycle latency
	.prog_full( pipe_fifo_full ),
	.prog_empty_thresh( 11'd511 ), // asserted by read operation when word count <= this, one cycle latency
	.prog_empty( pipe_fifo_empty ),
	.empty( ),
	.full( ) );

//------------------------------------------------------------------------
// SDRAM CONTROLLER
//------------------------------------------------------------------------
assign sdram_cke = 1'b1;
assign sdram_ldqm = 1'b0;
assign sdram_udqm = 1'b0;


// pipeline stage for sdram_reset. Required to match timing with 111 MHz
reg		sdramctrl_reset_pipe;
always @(negedge sdramclk ) begin
	sdramctrl_reset_pipe <= sdramctrl_reset;
end

sdramctrl c0 (
		.clk( ~sdramclk ), //
		.clk_read( ~sdramclk ), //
		.reset( sdramctrl_reset_pipe ),
		.cmd_pagewrite( cmd_pagewrite ),
		.cmd_pageread( cmd_pageread ),
		.cmd_ack( cmd_ack ),
		.cmd_done( cmd_done ),
		.write_address( write_address ),
		.read_address( read_address ),
		.fifo_din( pipe_fifo_dout ),
		.fifo_read( pipe_fifo_rd_en ),
		.fifo_dout( decoder_fifo_din ),
		.fifo_write( decoder_fifo_wr_en ),
		.sdram_cmd({sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}),
		.sdram_ba(sdram_ba),
		.sdram_a(sdram_a),
		.sdram_d(sdram_d));

//------------------------------------------------------------------------
// Decoder
//------------------------------------------------------------------------

Decoder #(
	.mult_width( 32 ),
	.pattern_width( 32 ) )
decoder (
	.clk( clk1x ),
	.reset( decoder_reset ),
	.reset_value( decoder_reset_value),
	.mult( {decoder_fifo_dout[47:32],decoder_fifo_dout[63:48]} ),
	.pattern( decoder_fifo_dout[31:0] ),
	.rd_en( decoder_fifo_rd_en ),
	.dout( decoder_dout ));

//------------------------------------------------------------------------
// output serializer
//------------------------------------------------------------------------
Serializer8 ch0 (
	.clk1x(clk1x),
	.clk2x(clk2x),
	.clk4x(clk4x),
	.din(decoder_dout[7:0]),
	.dout(dout[0]));
	
Serializer8 ch1 (
	.clk1x(clk1x),
	.clk2x(clk2x),
	.clk4x(clk4x),
	.din(decoder_dout[15:8]),
	.dout(dout[1]));
	
Serializer8 ch2 (
	.clk1x(clk1x),
	.clk2x(clk2x),
	.clk4x(clk4x),
	.din(decoder_dout[23:16]),
	.dout(dout[2]));
	
Serializer8 ch3 (
	.clk1x(clk1x),
	.clk2x(clk2x),
	.clk4x(clk4x),
	.din(decoder_dout[31:24]),
	.dout(dout[3]));

//------------------------------------------------------------------------
// Opal Kelly Host
//------------------------------------------------------------------------
okHost okHI	(	.hi_in( hi_in ),
				.hi_out( hi_out ),
				.hi_inout( hi_inout ),
				.ti_clk( ti_clk ),
				.ok1( ok1 ),
				.ok2( ok2 )
			);
   
//------------------------------------------------------------------------
// Opal Kelly output multiplexer
//------------------------------------------------------------------------
okWireOR #(.N(N)) wireOR ( ok2, ok2x );        // N = 3: 1 x okBTPipeOut pipe, 2 x okWireOut
   
//------------------------------------------------------------------------
// Opal Kelly pipe from PC
//------------------------------------------------------------------------
okBTPipeIn pipe (
	.ok1( ok1 ),
	.ok2( ok2x[ 0*17 +: 17 ] ),
	.ep_addr( 8'h80 ),
	.ep_dataout( pipe_dout ),
	.ep_write( pipe_fifo_wr_en ),
	.ep_blockstrobe(  ),
	.ep_ready( ~pipe_fifo_full ) );

//------------------------------------------------------------------------
// Opal Kelly wires from PC
//------------------------------------------------------------------------
okWireIn wire_in (	.ok1(ok1),
					.ep_addr(8'h00),
					.ep_dataout( {decoder_reset_value[3:0],decoder_reset,command[2:0]} )
					);

//------------------------------------------------------------------------
// Opal Kelly trigger to PC
//------------------------------------------------------------------------
okTriggerOut trigger_out (	.ok1(ok1),
							.ok2( ok2x[ 1*17 +: 17 ] ),
							.ep_addr( 8'h60 ),
							.ep_clk( clk1x ),
							.ep_trigger( decoder_fifo_underflow )
							);

//------------------------------------------------------------------------
// DCM
//------------------------------------------------------------------------

DCM_SP #(
	.CLK_FEEDBACK 	("1X"),
	.CLKDV_DIVIDE	(2.0),
	.CLKIN_PERIOD	("6.0"),
	.DESKEW_ADJUST	("0"),	
	.CLKFX_MULTIPLY	(2),
	.CLKFX_DIVIDE	(3))	
dcm_clk (
	.CLKIN   	(clk2xindcm),
	.CLKFB   	(clk2x),
	.DSSEN 		(1'b0),
	.PSINCDEC	(1'b0),
	.PSEN 		(1'b0),
	.PSCLK 		(1'b0),
	.RST     	(1'b0),
	.CLK0    	(clk2xdcm),
	.CLK90   	(),
	.CLK180  	(),
	.CLK270  	(),
	.CLK2X   	(clk4xdcm),
	.CLK2X180	(),
	.CLKDV   	(clk1xdcm),
	.CLKFX   	(clkfxdcm),
	.CLKFX180	(),
	.LOCKED  	(),
	.PSDONE  	(),
	.STATUS  	());

//------------------------------------------------------------------------
// clock buffer
//------------------------------------------------------------------------
BUFG bufg_2xin	( .I(clk2xin),		.O(clk2xindcm) ) ; // clock buffer between clock input pad and dcm 
BUFG bufg_1x	( .I(clk1xdcm),		.O(clk1x) ) ;
BUFG bufg_2x	( .I(clk2xdcm),		.O(clk2x) ) ;
BUFG bufg_4x	( .I(clk4xdcm),		.O(clk4x) ) ;
BUFG bufg_sdram	( .I(clkfxdcm),		.O(sdramclk) ) ;

//------------------------------------------------------------------------
// source synchronous SDRAM clock output
//------------------------------------------------------------------------
OFDDRCPE sdramOutputClock (
	.Q( sdram_clk_out ),
	.C0( sdramclk ), //
	.C1( ~sdramclk ), //
	.CE(1'b1),
	.CLR(1'b0),
	.D0(1'b1),
	.D1(1'b0),
	.PRE(1'b0) );

endmodule
